using System;
using System.Collections.Generic;
using System.Text;
using BuildingBasicDomain;

namespace BuildingStructuralElements.Loads
{   
    public delegate double[] distributionFunction(double position);  

    [Serializable ]
    public class DistributedLoad :Load   
    {
		NodalLoad load1;
        public NodalLoad Load1
		{
			set {
				load1 = value;
			}
			
			get {
				return load1;
			}
		}

        NodalLoad load2;
        public NodalLoad Load2
        {
            set
            {
                load2 = value;
            }

            get
            {
                return load2;
            }
        }
        
        public double Length
        {
            get
            {
                if (BeamElement != null)
                    return BeamElement.Length();
                else return -1;
               // else throw new StructuralException("Distributed Load Is Not Applied on Any Element");
            }
        }

        private double offsetLeft;
        public double OffsetLeft
        {
            get { return offsetLeft; }
            set { offsetLeft = value; }
        }

        private double offsetRight;
        public double OffsetRight
        {
            get { return offsetRight; }
            set { offsetRight = value; }
        }

        private distributionFunction _destributionFunction;

        private ILine _beamElement;
        public ILine BeamElement
        {
            get { return _beamElement; }
            set {  _beamElement = value ; }
        }

        public DistributedLoad(NodalLoad  load1, NodalLoad  load2, ILine beamElement):this(load1 ,load2 )
        {
  
            this._beamElement = beamElement;              
        }

        public DistributedLoad(NodalLoad load1, ILine beamElement) : 
            this(load1, load1, beamElement) { }

        public DistributedLoad(NodalLoad load1, NodalLoad load2)
        {
            SetLoads(load1, load2); 
        }

        public DistributedLoad(DistributedLoad distributedLoad)
        {
            this.load1 = new NodalLoad(distributedLoad.load1);
            this.load2 = new NodalLoad(distributedLoad.load2);
            this.LC = distributedLoad.LC;
            this.DestributionFunction = new distributionFunction(distributedLoad.DestributionFunction);
            if (_beamElement != null)
                this.BeamElement = distributedLoad.BeamElement;

        }

        private void SetLoads(NodalLoad load1, NodalLoad load2)
        {
            if (load1.Node != null) { load1.Node = null; }
            if (load2.Node != null) { load2.Node = null; } 
            this.Load1 = load1;
            this.Load2 = load2;
            DestributionFunction = linearFunction;
        }
        public distributionFunction DestributionFunction

        {
            get
            {
                return _destributionFunction;
            }
            set
            {
                _destributionFunction = new distributionFunction(value);
            }
        }

        public double[] GetIntensityAt(double position)
        {
            if ((position - Length) > 0.1 || position < 0)
                return new double[] { 0, 0, 0, 0, 0, 0 };
            return _destributionFunction(position);     
        }

        protected double [] linearFunction(double position)
        {
           double[] load = new double [6];

           load[0] = linearInterpolation(position, Load1.Fx, Load2.Fx, Length);
           load[1] = linearInterpolation(position, Load1.Fy, Load2.Fy, Length);
           load[2] = linearInterpolation(position, Load1.Fz, Load2.Fz, Length);

           load[3] = linearInterpolation(position, Load1.Mx, Load2.Mx, Length);
           load[4] = linearInterpolation(position, Load1.My, Load2.My, Length);
           load[5] = linearInterpolation(position, Load1.Mz, Load2.Mz, Length);
 
            return load;
        }

        private double linearInterpolation(double position , double f1 , double f2, double lenght)
        {
            return  f1 + (f2 - f1) * (position / Length );
        }

        private Load[] GetNodalsLoad()
        {
            // here is covered only the case of uniformely distributed load 
            // the general case must compute the    N(x)*f(x) integral N(x) the linear 
            // shape  function . use Simpson integration rule from Math library .

            Load[] nodal = new NodalLoad[2];
            double load;

            for (int i = 0; i < 6; i++)
            {
                load = TotalLoad(i) / 2;
                nodal[0] = new NodalLoad(BeamElement.SNode);
                ((NodalLoad)nodal[0])[i] = load;
                nodal[1] = new NodalLoad(BeamElement.ENode);
                ((NodalLoad)nodal[1])[i] = load;
            }
            return nodal;
        }

        public  override Load[] Distribute()
        {
            return GetNodalsLoad();
        }

        public override double TotalLoad(int direction)
        {
            // use numerical integration with _destributionFunction .
           return  (Load1[direction] + Load2 [direction]) * Length / 2;
        }

        public IList <Load > DistributeLoadOnSegments()
        {
            if (this.BeamElement is BeamElement)
            {
                if (((BeamElement)this.BeamElement).Segments != null)
                    if (((BeamElement)this.BeamElement).Segments.Count > 0)
                    {
                        double dist1, dist2; IList<Load> segmentLoads = new List<Load>();
                        foreach (BeamElement segment in ((BeamElement)this.BeamElement).Segments)
                        {
                            dist1 = segment.Line.DistenceToSatrtNode(segment.SNode);
                            dist2 = segment.Line.DistenceToSatrtNode(segment.ENode);

                            double[] loadIntensity1 = this.GetIntensityAt(dist1);
                            double[] loadIntensity2 = this.GetIntensityAt(dist2);

                            NodalLoad load1 = new NodalLoad(loadIntensity1, segment.SNode);
                            NodalLoad load2 = new NodalLoad(loadIntensity1, segment.ENode);

                            DistributedLoad segmentLoad = new DistributedLoad(load1, load2, segment);

                            segmentLoad.IsSelected = this.isSelected;
                            segmentLoad.LC = this.LC;

                            segmentLoads.Add(segmentLoad);
                        }

                        AssertLoadPreservation(segmentLoads);
                        return segmentLoads;
                    }
            }
            return null;
        }

        [System .Diagnostics .Conditional ("DEBUG")]
        private void AssertLoadPreservation(IList<Load> segmentLoads)
        {
            double TotalLoadsegments = 0;
            foreach (Load load in segmentLoads)
            { TotalLoadsegments += load.TotalLoad(1); }
            //  TotalLoadsegments = this.TotalLoad(1);
            if (this.TotalLoad(1) != TotalLoadsegments)
                System.Diagnostics.Trace.TraceWarning("Load " + this.id + " did not preserved");
        }
    }
}
